package com.lianxi.hibernate.dao.impl;


import com.lianxi.hibernate.dao.IBaseDao;
import com.lianxi.hibernate.wang.Page;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import java.lang.reflect.ParameterizedType;
import java.math.BigInteger;
import java.util.List;

@Transactional
public class BaseDao<T> implements IBaseDao<T> {

    private SessionFactory sessionFactory;
    /**
     * 创建一个Class的对象来获取泛型的class
     */
    private Class<?> clz;

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    @Inject
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public Session getSession() {

//        return sessionFactory.openSession();
        return sessionFactory.getCurrentSession();
    }

    private Class<?> getClz() {
        if (clz == null) {
            // 获取泛型的Class对象
            clz = ((Class<?>) (((ParameterizedType) (this.getClass()
                    .getGenericSuperclass())).getActualTypeArguments()[0]));
        }
        return clz;
    }


    @Override
    public int add(T t) {
        int id = 0;
        try {
            id = (int) this.getSession().save(t);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return id;
    }

    @Override
    public void update(T t) {
        this.getSession().merge(t);
    }

    @Override
    public T get(int id) {
        return (T) this.getSession().get(getClz(), id);
    }

    @Override
    public T load(int id) {
        return (T) this.getSession().load(getClz(), id);
    }

    @Override
    public void delete(int id) {
        this.getSession().delete(this.load(id));
    }


    public List<T> list(String hql, Object[] args) {
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        return query.list();
    }

    public List<T> list(String hql, Object param) {
        return this.list(hql, new Object[]{param});
    }

    public List<T> list(String hql) {
        return this.list(hql, null);
    }

    public List<T> list(String hql, Object[] args, int first, int end) {
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        return query.setFirstResult(first).setMaxResults(end).list();
    }

    public Object loadEntity(int id, Class clz) {
        return getSession().load(clz, id);
    }

    public List<?> listObj(String hql, Object[] args) {
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        return query.list();
    }

    public List<?> listObj(String hql, Object obj) {
        return this.listObj(hql, new Object[]{obj});
    }

    public List<?> listObj(String hql) {
        return this.listObj(hql, null);
    }


    public List<T> list(String hql, Object obj, int first, int end) {
        return this.list(hql, new Object[]{obj}, first, end);
    }

    public List<T> list(String hql, int first, int end) {
        return this.list(hql, null, first, end);
    }

    public Object queryObject(String hql, Object[] args) {
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        return query.uniqueResult();
    }

    public Object queryObject(String hql, Object param) {
        return this.queryObject(hql, new Object[]{param});
    }

    public Object queryObject(String hql) {
        return this.queryObject(hql, null);
    }

    /**
     * 更新数据
     *
     * @param hql
     * @param args
     */
    public void updateByHql(String hql, Object[] args) {
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        query.executeUpdate();
    }

    public void updateByHql(String hql, Object param) {
        this.updateByHql(hql, new Object[]{param});
    }

    public void updateByHql(String hql) {
        this.updateByHql(hql, null);
    }

    /**
     * sql查询
     *
     * @param sql
     * @param args
     * @param clz
     * @param hasEntity 是否是实体
     * @return
     */
    @SuppressWarnings("unchecked")
    public <N extends Object> List<N> listBySql(String sql, Object[] args,
                                                Class<?> clz, boolean hasEntity) {

//        NativeQuery  sq = this.getSession().createNativeQuery(sql,clz);


        SQLQuery sq = this.getSession().createSQLQuery(sql);
        setParameter(sq, args);
        if (hasEntity) {
            sq.addEntity(clz);
        } else {
            sq.setResultTransformer(Transformers.aliasToBean(clz));
        }
        return sq.list();
    }

    public <N extends Object> List<N> listBySqlAndList(String sql, List<Object> list,
                                                       Class<?> clz, boolean hasEntity) {

        SQLQuery sq = this.getSession().createSQLQuery(sql);
        setParameterQuery(sq, list);
        if (hasEntity) {
            sq.addEntity(clz);
        } else {
            sq.setResultTransformer(Transformers.aliasToBean(clz));
        }
        return sq.list();
    }

    public <N extends Object> List<N> listBySql(String sql, Object param,
                                                Class<?> clz, boolean hasEntity) {
        return this.listBySql(sql, new Object[]{param}, clz, hasEntity);
    }

    public <N extends Object> List<N> listBySql(String sql, Class<?> clz,
                                                boolean hasEntity) {
        return this.listBySql(sql, null, clz, hasEntity);
    }

    public Long getCount(String hql, Object[] objs) {
        Query query = this.getSession().createQuery(hql);
        setParameter(query, objs);
        return (Long) query.uniqueResult();
    }

    public Long getCount(String hql, Object obj) {
        return this.getCount(hql, new Object[]{obj});
    }

    public Long getCount(String hql) {
        return this.getCount(hql, null);
    }


    public List<T> listLimit(String hql, Object[] args, int page, int row) {
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        query.setFirstResult((page - 1) * row).setMaxResults(row);
        return query.list();
    }

    public List<T> listLimit(String hql, Object obj, int page, int row) {
        return this.listLimit(hql, new Object[]{obj}, page, row);
    }

    public List<T> listLimit(String hql, int page, int row) {
        return this.listLimit(hql, null, page, row);
    }

    /**
     * 直接计算，不用按页数来分了
     *
     * @param hql
     * @param args
     * @param start
     * @param pageSize
     * @return
     */
    public List<T> findByPage(String hql, Object[] args, int start, int pageSize) {
        Query query = this.getSession().createQuery(hql);
        setParameter(query, args);
        query.setFirstResult(start).setMaxResults(pageSize);
        return query.list();
    }

    public List<T> findByPage(String hql, Object obj, int page, int pageSize) {
        return this.findByPage(hql, new Object[]{obj}, page, pageSize);
    }

    public List<T> findByPage(String hql, int page, int pageSize) {
        return this.findByPage(hql, null, page, pageSize);
    }

    public void executeSQL(String sql, Object[] params) {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        setParameter(query, params);
        query.executeUpdate();
        this.getSession().flush();
        this.getSession().clear();
    }

    public Long countBySQL(String sql, Object[] params) {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        setParameter(query, params);
        BigInteger count = (BigInteger) query.uniqueResult();
        this.getSession().flush();
        this.getSession().clear();
        if (count != null) {
            return count.longValue();
        } else {
            return 0L;
        }
    }

    /**
     * 根据顺序来设置参数
     *
     * @param query
     * @param args
     */
    private void setParameter(Query query, Object[] args) {
        if (args != null && args.length > 0) {
            int index = 0;
            for (Object arg : args) {
                query.setParameter(index++, arg);
            }
        }
    }

    private void setParameterQuery(Query query, List<Object> list) {
        if (null != list) {
            int len = list.size();
            if (len > 0) {
                for (int i = 0; i < len; i++) {
                    query.setParameter(i, list.get(i));
                }
            }
        }
    }

    private Query setParameterQuery(String hql, Object[] objs) {
        Query q = this.getSessionFactory().getCurrentSession().createQuery(hql);
        if (objs != null) {
            for (int i = 0; i < objs.length; i++) {
                q.setParameter(i, objs);
            }
        }
        return q;
    }

    private String getCountHql(String hql, boolean isHql) {
        String e = hql.substring(hql.indexOf("from"));
        String c = "select count(id) " + e;
        if (isHql) {
            c.replaceAll("fetch", "");
        }
        return c;
    }

    /**
     * 分页查询
     *
     * @param hql
     * @param params
     * @param page
     * @return
     */
    public Page findByPager(String hql, Object[] params, Page page) {
        int pageNumber = page.getPageNumber();
        int length = page.getPageSize();
        List<T> list = findByPage(hql, params, (pageNumber - 1) * length, length);

        page.setDatas(list);
        //获得总行数
        hql = getCountHql(hql, true);
        Long count = this.getCount(hql, params);
        page.setTotalRow(count.intValue());

        page.setTotalPage((count.intValue() + length - 1) / length);
        return page;
    }


    /**
     * @param hql      hql语句
     * @param args     参数
     * @param pageNum  当前页码
     * @param pageSize 页面大小
     * @return
     */
    public Page<T> find(String hql, Object[] args, Integer pageNum, Integer pageSize) {
        List<T> datas = this.findByPage(hql, args, (pageNum - 1) * pageSize, pageSize);
        String cql = getCountHql(hql, true);
        Long total = getCount(cql, args);
        Page<T> pages = new Page<>();
        pages.setDatas(datas);
        pages.setTotalRow(total.intValue());
        pages.setTotalPage((int) Math.ceil((total + pageSize - 1) / pageSize));
        return pages;
    }

}
